__all__ = ['JDict', 'JsonFile', 'InfiniteDict', 'TaskList']

import asyncio
import json
import os
from collections import defaultdict
from typing import Coroutine


class JDict(dict):
    def __getattr__(self, key):
        return self[key]


class JsonFile(dict):
    def __init__(self, filename):
        self.name = filename
        if os.path.exists(filename):
            with open(filename, 'r', encoding='utf-8') as f:
                dict.__init__(self, json.load(f))
        else:
            self.update()

    def update_file(self):
        with open(self.name, 'w', encoding='utf-8') as f:
            json.dump(self, f, ensure_ascii=False)


class InfiniteDict(defaultdict):
    def __init__(self, source=None):
        super().__init__(InfiniteDict)
        if source is not None:
            for k, v in source.items():
                if isinstance(v, dict):
                    v = InfiniteDict(v)
                self[k] = v

    def __repr__(self):
        return str({k: v for k, v in self.items()})

    def __delitem__(self, key):
        if not super().__contains__(key):
            return
        super().__delitem__(key)

    def check(self):
        for key, value in dict(self).items():
            if isinstance(value, InfiniteDict):
                value.check()
                if not value:
                    super().__delitem__(key)


class TaskList:
    def __init__(self, loop: asyncio.AbstractEventLoop):
        self.__loop = loop
        self.__tasks = set()

    def add_task(self, co: Coroutine):
        task = self.__loop.create_task(co)
        task.add_done_callback(self.__tasks.remove)
        self.__tasks.add(task)
        return task

    async def wait(self):
        return await asyncio.gather(*self.__tasks)
